using Pinwheel.Service;

namespace Pinwheel.Kernel
{
    public sealed partial class ServiceManager
    {
        private WorkProcess[] m_WorkProcesses;
        
        private int m_WorkProcessIndex = 0;
        private int m_WorkProcessMask;
        
        public int WorkProcessIndex => Interlocked.Increment(ref m_WorkProcessIndex) & m_WorkProcessMask;
        public int WorkProcessCount => m_Config.WorkProcessCount;
        
        internal WorkProcess[] WorkProcesses => m_WorkProcesses;
        internal WorkProcess WorkProcess => m_WorkProcesses[WorkProcessIndex];
        internal TaskFactory TaskFactory => WorkProcess.TaskFactory;
        
        void InitWorkProcess()
        {
            m_WorkProcesses = new WorkProcess[m_Config.WorkProcessCount];
            for (int i = 0; i < m_Config.WorkProcessCount; i++)
            {
                m_WorkProcesses[i] = new WorkProcess(this, i);
                m_WorkProcesses[i].Start();
            }
            m_WorkProcessMask = (1 << (int) Math.Floor(Math.Log(m_Config.WorkProcessCount, 2))) - 1;
        }

        // public async Task<IMessage> Call(PID pid, IMessage message)
        // {
        //     message.Header.Session = 1;
        //     var task = m_TaskFactories[WorkProcessIndex].StartNew(pid.SendAsync);
        // }


        #region Local

        public void Send(PID source, string alias, string method, MessageType type, IMessage message)
        {
            Send(source, GetServicePID(alias), method, type, message);
        }
        
        public void Send(PID source, PID dest, string method, MessageType type, IMessage message)
        {
            TaskFactory.StartNew(() => dest.Send(this, source, Global.DefaultService, method, type, message));
        }
        
        public Task<IMessage> Call(PID source, string alias, string method, MessageType type, IMessage message, int timeout = 5000)
        {
            return Call(source, GetServicePID(alias), method, type, message, timeout);
        }
        
        public Task<IMessage> Call(PID source, PID dest, string method, MessageType type, IMessage message, int timeout = 5000)
        {
            return TaskFactory.StartNew(() => dest.Call(this, source, Global.DefaultService, method, type, message, timeout)).Unwrap();
        } 

        #endregion
        
        
        #region Remote
        
        public void Send(PID source, string node, string alias, string method, IMessage message)
        {
            Send(source, node, alias, method, MessageType.Service, message);
        }
        
        public void Send(PID source, string node, string alias, string method, MessageType type, IMessage message)
        {
            var cluster = GetServicePID(Cluster.Name);
            TaskFactory.StartNew(() => cluster.Send(this, source, Global.DefaultService, "Send", type, new RemoteCall()
            {
                Node = node,
                Service = alias,
                Method = method,
                Type = type,
                Message = message,
            }));
        }
        
        public Task<IMessage> Call(PID source, string node, string alias, string method, IMessage message, int timeout = 5000)
        {
            return Call(source, node, alias, method, MessageType.Service, message, timeout);
        }
        
        public Task<IMessage> Call(PID source, string node, string alias, string method, MessageType type, IMessage message, int timeout = 5000)
        {
            var cluster = GetServicePID(Cluster.Name);
            return TaskFactory.StartNew(() => cluster.Call(this, source, Global.DefaultService, "Call", MessageType.Service, new RemoteCall()
            {
                Node = node,
                Service = alias,
                Method = method,
                Type = type,
                Message = message,
            }, timeout)).Unwrap();
        }
        
        #endregion
        
    }
}